﻿using System.Collections;
using System.Collections.Generic;
using ActionLogic;
using System;
using BehaviorModel.Block.ServiceChannel;
using ComputationalCore.Helper;
using Model;
using System.Linq;

namespace BehaviorModel
{
    public enum ChoosingDiscipline
    {
        Fifo,
        Lifo,
        Random
    }

    /// <summary>
    /// Describes request buffer implementation.
    /// </summary>
    public class RequestBuffer : IRequestBuffer
    {
        #region Fields

        private LinkedList<Request> _buffer;
        private event Action _onRequestAdded;

        #endregion

        #region Properties

        public int MaxCapacity { get; set; }

        public int MaxWaitingTime { get; set; }

        public int Count
        {
            get { return _buffer.Count; }
        }

        public ChoosingDiscipline Discipline { get; set; }

        public event Action OnRequestAdded
        {
            add
            {
                if (_onRequestAdded != null)

                    if (_onRequestAdded.GetInvocationList().Any(handler => handler.Target == value.Target))
                    {
                        return;
                    }
                _onRequestAdded += value;
            }
            remove { if (_onRequestAdded != null) _onRequestAdded -= value; }
        }

        public event Action<Request> OnRequestRejected;

        #endregion

        #region Constructor

        public RequestBuffer()
        {
            _buffer = new LinkedList<Request>();
            MaxCapacity = 0;
            MaxWaitingTime = 0;
            Discipline = ChoosingDiscipline.Fifo;
        }

        #endregion

        #region Methods

        public void Add(Request request)
        {
            if (0 == MaxCapacity || _buffer.Count < MaxCapacity)
            {
                _buffer.AddLast(request);

                if (_onRequestAdded != null)
                {
                    _onRequestAdded();
                }
                return;
            }

            if (OnRequestRejected != null)
            {
                OnRequestRejected(request);
            }
        }

        public Request Get()
        {
            Request request = null;
            var isCorrectRequest = false;

            while (!IsEmtpy() && !isCorrectRequest)
            {
                switch (Discipline)
                {
                    case ChoosingDiscipline.Fifo:
                        request = _buffer.First();
                        _buffer.RemoveFirst();
                        break;
                    case ChoosingDiscipline.Lifo:
                        request = _buffer.Last();
                        _buffer.RemoveLast();
                        break;
                    case ChoosingDiscipline.Random:
                        int index = RandomHelper.NextInt(0, _buffer.Count);
                        request = _buffer.ElementAt(index);
                        _buffer.Remove(request);
                        break;
                }

                isCorrectRequest = !IsWaitingTimeOver(request);
                if (!isCorrectRequest)
                {
                    OnRequestRejected(request);
                }
            }

            return (isCorrectRequest) ? request : null;
        }

        public IEnumerable<Request> GetAllRequest()
        {
            var requests = new List<Request>(_buffer);
            _buffer.Clear();

            return requests;
        }

        public bool IsEmtpy()
        {
            return _buffer.Count == 0;
        }

        public void Clear()
        {
            _buffer.Clear();
        }

        public void UpdateWaitingQueue(double waitingTime)
        {
            for (var i = _buffer.Count - 1; i >= 0; i--)
            {
                var request = _buffer.ElementAt(i);
                request.LastQueueWaitingTime += waitingTime;
                if (!IsWaitingTimeOver(request))
                {
                    continue;
                }

                _buffer.Remove(request);
                if (OnRequestRejected != null)
                {
                    OnRequestRejected(request);
                }
            }
        }

        private bool IsWaitingTimeOver(Request request)
        {
            return (request == null) || (MaxWaitingTime != 0 && request.LastQueueWaitingTime >= MaxWaitingTime);
        }

        #endregion
    }
}